SpringBoot+JWT实现登录状态管理(任意位置获得用户信息)

SpringBoot+JWT实现登录状态管理

介绍

​ 本文实现登录状态管理采用如下技术,全局异常中心、全局获得用户信息、自定义返回类。
项目地址:https://gitee.com/jiang_ming_hong/spring-boot–jwt.git

实现过程

1.需要使用的依赖包

		 <!--web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--JWT的依赖-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
          <!--lombok-->
		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2.编写自定义返回类

public class R {
    private static HashMap map = new HashMap();

    public static Map succeed(String msg) {
        map.put("msg", msg);
        map.put("code", 200);
        return map;
    }

    public static Map succeed(String msg, Object data) {
        map.put("code", 200);
        map.put("msg", msg);
        map.put("data", data);
        return map;
    }

    public static Map succeed(int code, String msg, Object data) {
        map.put("msg", msg);
        map.put("data", data);
        map.put("code", code);
        return map;
    }

}

3.全局异常类

全局异常中心就是把异常集中在一个类中,方便管理异常。

@Configuration
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
public class GlobalException {

    @ExceptionHandler(SignatureVerificationException.class)
    public Map testException(SignatureVerificationException signatureVerificationException) {
        return R.succeed("签名异常");
    }

    @ExceptionHandler(TokenExpiredException.class)
    public Map testException(TokenExpiredException tokenExpiredException) {
        return R.succeed("token过期");
    }

    @ExceptionHandler(AlgorithmMismatchException.class)
    public Map testException(AlgorithmMismatchException AlgorithmMismatchException) {
        return R.succeed("加密算法不匹配");
    }

    @ExceptionHandler(Exception.class)
    public Map testException(Exception exception) {
        return R.succeed("出错了");
    }

}

4.全局用户信息类

根据token值取出用户的信息,当需要用户这些信息时直接取就行,不用从数据库中取。

public class BaseUserInfo {

    private static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new ThreadLocal<>();

    //判断线程map是否为空,为空就添加一个map
    public static Map<String, String> getLocalMap() {
        Map<String, String> map = THREAD_LOCAL.get();
        if (map == null) {
            map = new HashMap<>(10);
            THREAD_LOCAL.set(map);
        }
        return map;
    }
    //把用户信息添加到线程map中
    public static void set(String key, String name) {
        Map<String, String> map = getLocalMap();
        map.put(key, name);
    }
    //获得线程map中的数据
    public static String get(String key) {
        Map<String, String> map = getLocalMap();
        return map.get(key);
    }
}

5.JWT封装类

@Slf4j
public class JWTUtils {
    /**
     * 盐值
     */
    private static final String SING="123456";


    /**
     * 生成令牌
     * @param map payload载荷声明参数
     * @return
     */
    public  static String getToken(Map<String,String> map){
        //获取日历对象
        Calendar calendar=Calendar.getInstance();
        //默认7天过期
        calendar.add(Calendar.DATE, 7);
        //新建一个JWT的Builder对象
        JWTCreator.Builder builder = JWT.create();
        //将map集合中的数据设置进payload
        map.forEach((k,v)->{
            builder.withClaim(k, v);
        });
        //设置过期时间和签名
        log.info("时间={}"+calendar.getTime());
        String sign = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SING));

        return sign;
    }

    /**
     * 验签并返回DecodedJWT
     * @param token  令牌
     */
    public  static DecodedJWT getTokenInfo(String token){
       return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
    }
}

6.用户枚举

@Getter
public enum UserInfoEnu {
    user("name"),
    pass("pass");
    private String code;

    UserInfoEnu(String code) {
        this.code = code;
    }
}

7.配置拦截器

控制那些请求被拦截或者那些请求不被拦截

@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {

    /**
     * 自定义拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyHandler())
                //拦截全部
                .addPathPatterns("/**")
                //不需拦截的请求
                .excludePathPatterns(this.exclud());
    }
    /**
     *     不需拦截的请求
     */
    public String[] exclud(){
        String[] urls= {
                "/login"
        };
        return urls;
    }
}

8.编写自定义拦截器

验证Token信息是否正确,不正确提示相应的错误

public class MyHandler implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        //获得请求头里的TOKEN数据
        String token = request.getHeader("token");
        //根据token解析数据,因为配置了全局异常处理中心,如果有异常会在全局异常中心处理异常
        DecodedJWT tokenInfo = JWTUtils.getTokenInfo(token);
        //获得用户信息
        String name = tokenInfo.getClaim("name").asString();
        String pass = tokenInfo.getClaim("pass").asString();
        //存储用户信息到ThreadLocal中
        BaseUserInfo.set("name",name);
        BaseUserInfo.set("pass",pass);
        return true;
    }
 }
}

9.请求类

getBaseUserInfo()方法中BaseUserInfo.get()可以获得用户信息。

@RestController
public class testController {


    /**
     * 获得用户信息
     *
     * @return
     */
    @PostMapping("test")
    public String getBaseUserInfo() {
    //获得用户信息
        String name = BaseUserInfo.get(UserInfoEnu.user.getCode());
        String pass = BaseUserInfo.get(UserInfoEnu.pass.getCode());
        return "name:" + name + "-----pass:" + pass;
    }

    /**
     * 模拟用户登录
     *
     * @param name
     * @param pass
     * @return
     */
    @GetMapping("/login")
    public Map login(String name, String pass) {
        Map<String, String> map = new HashMap<>();
        map.put("name", name);
        map.put("pass", pass);
        String token = JWTUtils.getToken(map);
        return R.succeed(200, "登录成功", token);
    }

}

测试效果图

模拟登录

在这里插入图片描述

登录后获得用户信息

在用户登录成功后,因该把token值存在前端的请求头里。这里只是在Postman中请求。
用户一信息
在这里插入图片描述
用户二信息
在这里插入图片描述

把token值故意写错

可以看到提示未登录
在这里插入图片描述

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
单点登录(Single Sign-On,简称SSO)是一种身份验证技术,可以让用户只需一次登录,就可以访问多个应用程序。在实际开发中,我们可以使用Spring Boot、JWT和Redis来实现单点登录功能。 下面是实现单点登录的步骤: 1. 创建Spring Boot项目并引入所需依赖:spring-boot-starter-web、spring-boot-starter-data-redis和jjwt。 2. 创建一个User实体类,包含用户名和密码等信息。 3. 创建一个UserService,实现用户信息的操作,包括注册、登录等。 4. 引入JWT依赖后,我们需要创建一个JWTUtil类,实现token的生成和解析。 5. 创建一个LoginController,用于处理用户登录请求。在登录成功后,生成token并将其存储到Redis中。 6. 创建一个AuthController,用于验证用户的token是否有效。在验证成功后,可以获取用户信息并返回。 7. 在需要进行单点登录验证的应用程序中,只需要在请求中携带token,并调用AuthController进行验证即可。 具体实现细节可以参考以下代码示例: User实体类: ```java public class User { private String username; private String password; // 省略setter和getter方法 } ``` UserService接口: ```java public interface UserService { void register(User user); String login(String username, String password); } ``` UserService实现类: ```java @Service public class UserServiceImpl implements UserService { @Autowired private RedisTemplate<String, String> redisTemplate; @Override public void register(User user) { // 省略用户注册逻辑 } @Override public String login(String username, String password) { // 省略用户登录逻辑 // 登录成功后生成token并存储到Redis中 String token = JWTUtil.generateToken(username); redisTemplate.opsForValue().set(username, token, 30, TimeUnit.MINUTES); return token; } } ``` JWTUtil类: ```java public class JWTUtil { private static final String SECRET_KEY = "my_secret_key"; private static final long EXPIRATION_TIME = 3600000; public static String generateToken(String username) { return Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public static String getUsernameFromToken(String token) { return Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody() .getSubject(); } } ``` LoginController: ```java @RestController public class LoginController { @Autowired private UserService userService; @PostMapping("/login") public ResponseEntity<String> login(@RequestBody User user) { String token = userService.login(user.getUsername(), user.getPassword()); return ResponseEntity.ok(token); } } ``` AuthController: ```java @RestController public class AuthController { @Autowired private RedisTemplate<String, String> redisTemplate; @GetMapping("/auth") public ResponseEntity<User> auth(@RequestHeader("Authorization") String token) { String username = JWTUtil.getUsernameFromToken(token); if (StringUtils.isEmpty(username)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } String redisToken = redisTemplate.opsForValue().get(username); if (!token.equals(redisToken)) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } User user = new User(); user.setUsername(username); return ResponseEntity.ok(user); } } ``` 在请求中携带token的示例: ```java @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> { String token = // 从Redis中获取token request.getHeaders().add("Authorization", token); return execution.execute(request, body); })); return restTemplate; } } ``` 以上就是使用Spring Boot、JWT和Redis实现单点登录的步骤和示例代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值